/*
 * @(#)PaginationInfo.java $version 2012-3-5
 * 
 * Copyright 2011 RogueDevelopment Gruop. All rights Reserved.
 * ROGUEDEVELOPMENT PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nado.extensions.pagination;

import java.util.Iterator;
import java.util.LinkedHashMap;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

import com.nado.extensions.data.DataHandler;
import com.nado.extensions.data.DataHandlerFactory;
import com.nado.extensions.util.DataMap;

/**
 * @author LEESUNG
 * 
 */
public class PaginationInfo {
	public static final int DEFAULT_PAGE_SIZE = 10;
	public static final int DEFAULT_INDEX_SIZE = 10;
	public static final String STEP_TYPE_INDEX = "index";
	public static final String STEP_TYPE_PAGE = "page";
	public static final String DEFAULT_TEMPLATE_TYPE = "default";
	public static final String DEFAULT_PAGE_PARAMETER_NAME = "page";
	private int totalRows;
	private int pageSize = 10;
	private int indexSize = 10;
	private int page = 1;

	private String stepType = "page";
	private String requestURI = "";
	private String requestMethod = "GET";
	private String queryString = "";

	private String templateType = "default";

	private String pageParameterName = "page";

	public PaginationInfo() {
	}

	public PaginationInfo(String type, HttpServletRequest request, int totalRows) {
		if (StringUtils.isEmpty(type)) {
			throw new IllegalArgumentException("Pagination type is empty!");
		}

		String path = new StringBuilder().append("pagination/configs/config[@type='").append(type).append("']").toString();
		
		DataHandler handler = DataHandlerFactory.create("pagination-data.xml").select(path);
		
		int pageSize = NumberUtils.toInt(handler.get("page-size"), 10);
		int indexSize = NumberUtils.toInt(handler.get("index-size"), 10);
		String stepType = handler.get("step-type", "index");
		String templateType = handler.get("template-type", "default");

		int page = NumberUtils.toInt(
				request.getParameter(this.pageParameterName), 1);

		init(page, totalRows, pageSize, indexSize, stepType,
				request.getRequestURI(), request.getMethod(),
				request.getQueryString(), templateType);
	}

	public PaginationInfo(String pageParameterName, String type,
			HttpServletRequest request, int totalRows) {
		if (StringUtils.isEmpty(type)) {
			throw new IllegalArgumentException("Pagination type is empty!");
		}

		this.pageParameterName = pageParameterName;

		String path = new StringBuilder().append("pagination/configs/config[@type='").append(type).append("']").toString();
		
		DataHandler handler = DataHandlerFactory.create("pagination-data.xml").select(path);
		
		int pageSize = NumberUtils.toInt(handler.get("page-size"), 10);
		int indexSize = NumberUtils.toInt(handler.get("index-size"), 10);
		String stepType = handler.get("step-type", "index");
		String templateType = handler.get("template-type", "default");

		int page = NumberUtils.toInt(request.getParameter(pageParameterName), 1);

		init(page, totalRows, pageSize, indexSize, stepType, request.getRequestURI(), request.getMethod(), request.getQueryString(), templateType);
	}

	public PaginationInfo(int page, int totalRows, int pageSize, int indexSize) {
		init(page, totalRows, pageSize, indexSize, "index", "", "", "", "");
	}

	public PaginationInfo(int page, int totalRows, int pageSize, int indexSize,
			String stepType, String requestURI, String requestMethod,
			String queryString, String templateType) {
		init(page, totalRows, pageSize, indexSize, stepType, requestURI, requestMethod, queryString, templateType);
	}

	private void init(int page, int totalRows, int pageSize, int indexSize,String stepType, String requestURI, String requestMethod,String queryString, String templateType) {
		this.totalRows = adjustsValue(0, 2147483647, totalRows);
		this.pageSize = adjustsValue(1, 2147483647, pageSize);
		this.indexSize = adjustsValue(1, 2147483647, indexSize);

		this.page = adjustsValue(1, getPageCount(), page);

		this.stepType = StringUtils.defaultIfEmpty(stepType, "index");
		this.requestURI = StringUtils.defaultString(requestURI);
		this.requestMethod = StringUtils.defaultString(requestMethod, "GET");
		this.queryString = StringUtils.defaultString(queryString);
		this.templateType = StringUtils.defaultString(templateType, "default");
	}

	public int getTotalRows() {
		return this.totalRows;
	}

	public void setTotalRows(int totalRows) {
		this.totalRows = totalRows;
	}

	public int getPageSize() {
		return this.pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getIndexSize() {
		return this.indexSize;
	}

	public void setIndexSize(int indexSize) {
		this.indexSize = indexSize;
	}

	public int getPage() {
		return this.page;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public String getStepType() {
		return this.stepType;
	}

	public void setStepType(String stepType) {
		this.stepType = stepType;
	}

	public String getRequestURI() {
		return this.requestURI;
	}

	public void setRequestURI(String requestURI) {
		this.requestURI = requestURI;
	}

	public String getRequestMethod() {
		return this.requestMethod;
	}

	public void setRequestMethod(String requestMethod) {
		this.requestMethod = requestMethod;
	}

	public String getQueryString() {
		return this.queryString;
	}

	public void setQueryString(String queryString) {
		this.queryString = queryString;
	}

	public String getTemplateType() {
		return this.templateType;
	}

	public void setTemplateType(String templateType) {
		this.templateType = templateType;
	}

	public String getPageParameterName() {
		return this.pageParameterName;
	}

	public void setPageParameterName(String pageParameterName) {
		this.pageParameterName = pageParameterName;
	}

	public int getPageCount() {
		return ((this.totalRows - 1) / this.pageSize + 1);
	}

	public int getStepSize() {
		return (("index".equalsIgnoreCase(this.stepType)) ? this.indexSize : 1);
	}

	private int adjustsValue(int min, int max, int value) {
		if (value < min)
			return min;
		if (value > max) {
			return max;
		}
		return value;
	}

	public int getFirstPageInIndex() {
		return ((this.page - 1) / this.indexSize * this.indexSize + 1);
	}

	public int getLastPageInIndex() {
		return Math.min(getFirstPageInIndex() + this.indexSize - 1,
				getPageCount());
	}

	public int getFirstPage() {
		return 1;
	}

	public int getLastPage() {
		return getPageCount();
	}

	public int getPrevPage() {
		if (isFirstPage()) {
			return 0;
		}
		return Math.max(this.page - getStepSize(), 1);
	}

	public int getNextPage() {
		if (isLastPage()) {
			return 0;
		}
		return Math.min(this.page + getStepSize(), getLastPage());
	}

	public boolean hasPrevPage() {
		return (getPrevPage() > 0);
	}

	public boolean hasNextPage() {
		return (getNextPage() > 0);
	}

	public boolean isFirstPage() {
		return (this.page == getFirstPage());
	}

	public boolean isLastPage() {
		return (this.page == getLastPage());
	}

	public boolean isFirstPageInIndex() {
		return (this.page == getFirstPageInIndex());
	}

	public boolean isLastPageInIndex() {
		return (this.page == getLastPageInIndex());
	}

	public String getUrlFor(int page) {
		StringBuilder builder = new StringBuilder();
		builder.append(this.requestURI).append("?");
		builder.append(setPageParameter(this.queryString, page));
		return builder.toString();
	}

	protected String setPageParameter(String queryString, int page) {
		LinkedHashMap map = createQueryMap(queryString);
		map.put(this.pageParameterName, Integer.valueOf(page));

		StringBuilder builder = new StringBuilder();
		int i = 0;
		
		for(Iterator<String> all = map.keySet().iterator();all.hasNext();) {
			String key = all.next();
			if(i != 0) {
				builder.append('&');
			}
			builder.append(key).append('=').append(map.get(key));
			++i;
		}
		
		return builder.toString();
	}

	protected LinkedHashMap<String, Object> createQueryMap(String queryString) {
		LinkedHashMap map = new LinkedHashMap();

		if (StringUtils.isEmpty(queryString)) {
			return map;
		}

		String[] params = queryString.split("&");
		for (String eachParam : params) {
			if (StringUtils.isNotEmpty(eachParam)) {
				int idx = eachParam.indexOf(61);
				if (idx > 0) {
					map.put(StringUtils.left(eachParam, idx),
							StringUtils.substring(eachParam, idx + 1));
				}
			}
		}

		return map;
	}

	public int getStartRownum() {
		return ((this.page - 1) * this.pageSize + 1);
	}

	public int getEndRownum() {
		return Math.min(this.page * this.pageSize, this.totalRows);
	}
	
	public DataMap getPaginateMap() {
		DataMap model = new DataMap();
		model.put("isFirstPage", isFirstPage());
		model.put("hasPrevPage", hasPrevPage());
		model.put("prevPage", getPrevPage());
		model.put("firstPageInIndex", getFirstPageInIndex());
		model.put("lastPageInIndex", getLastPageInIndex());
		model.put("page", getPage());
		model.put("hasNextPage", hasNextPage());
		model.put("isLastPage", isLastPage());
		model.put("lastPage", getLastPage());
		model.put("nextPage", getNextPage());
		return model;
	}
}
